import tkinter as tk
from tkinter import ttk, messagebox, filedialog, simpledialog
import sys
import os
import json
from typing import List, Dict, Any, Optional
from datetime import datetime
import re

# 导入之前开发的模块
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from database_connection import DatabaseManager, DatabaseConfig, DatabaseType
from table_manager import TableManager, ColumnInfo
from data_manager import DataManager, QueryCondition, QueryOptions
from import_export_manager import ImportExportManager
from config_manager import ConfigManager  # 导入配置管理器

import threading
from concurrent.futures import ThreadPoolExecutor
import time


class DatabaseGUI:
    """数据库管理工具图形界面"""

    def __init__(self):
        self.root = tk.Tk()
        self.root.title("EasyDB")
        self.root.geometry("1200x800")

        # 设置 Windows AppUserModelID，改善任务栏图标与分组
        self.set_windows_app_user_model_id()

        # 设置应用图标（优先使用 assets/dog.ico，其次 assets/狗狗-copy-copy.png）
        self.set_app_icon()

        # 数据库相关对象
        self.db_manager = DatabaseManager()
        self.table_manager = None
        self.data_manager = None
        self.import_export_manager = None
        self.config_manager = ConfigManager()  # 初始化配置管理器
        self.current_table = None
        self.all_tables = []  # 保存完整表名列表用于检索
        self.table_columns_cache: Dict[str, List[str]] = {}

        # SQL 查询提示相关
        self.sql_keywords: List[str] = [
            'SELECT', 'FROM', 'WHERE', 'AND', 'OR', 'NOT', 'IN', 'LIKE', 'BETWEEN', 'IS', 'NULL', 'NOT NULL',
            'ORDER', 'BY', 'GROUP', 'HAVING', 'LIMIT', 'OFFSET', 'DISTINCT', 'AS', 'ON', 'JOIN', 'INNER', 'LEFT',
            'RIGHT', 'FULL', 'OUTER', 'CROSS', 'UNION', 'ALL', 'EXISTS', 'CASE', 'WHEN', 'THEN', 'ELSE', 'END',
            'INSERT', 'INTO', 'VALUES', 'UPDATE', 'SET', 'DELETE', 'CREATE', 'TABLE', 'DROP', 'ALTER', 'ADD',
            'PRIMARY', 'KEY', 'FOREIGN', 'REFERENCES', 'UNIQUE', 'INDEX'
        ]
        self.sql_suggest_win = None
        self.sql_suggest_listbox = None

        self.data_cache = {}  # 数据缓存
        self.current_page = 1
        self.page_size = 100
        self.total_records = 0
        self.loading = False
        self.executor = ThreadPoolExecutor(max_workers=2)

        # 创建界面
        self.create_widgets()
        self.update_connection_status()
        self.refresh_config_list()  # 初始化配置列表
        # 尝试自动连接上次成功的配置
        self.try_auto_connect_last_config()

    def set_app_icon(self):
        """设置应用图标为可爱的狗头（支持 .ico/.png，放置在 assets/ 目录）"""
        try:
            base_dir = os.path.dirname(os.path.abspath(__file__))
            assets_dir = os.path.join(base_dir, 'assets')
            ico_path = os.path.join(assets_dir, '狗狗-copy-copy.ico')
            png_path = os.path.join(assets_dir, '狗狗-copy-copy.png')

            # Windows 优先使用 .ico
            if os.path.exists(ico_path):
                try:
                    self.root.iconbitmap(default=ico_path)
                    return
                except Exception:
                    pass

            # 其他平台或 .ico 失败时尝试 .png
            if os.path.exists(png_path):
                try:
                    img = tk.PhotoImage(file=png_path)
                    # 需要保存引用避免被回收
                    self._app_icon_image = img
                    self.root.iconphoto(True, img)
                    return
                except Exception:
                    pass
        except Exception:
            pass

    def set_windows_app_user_model_id(self):
        """在 Windows 上设置 AppUserModelID，便于任务栏显示自定义图标与分组"""
        try:
            if sys.platform.startswith('win'):
                import ctypes
                app_id = u"DBTool.DogIcon.1.0"
                ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(app_id)
        except Exception:
            pass

    def create_widgets(self):
        """创建界面组件"""
        # 创建主框架
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # 创建顶部连接区域
        self.create_connection_frame(main_frame)

        # 创建主要内容区域
        self.create_main_content(main_frame)

        # 创建状态栏
        self.create_status_bar()

    def create_connection_frame(self, parent):
        """创建数据库连接配置区域"""
        conn_frame = ttk.LabelFrame(parent, text="数据库连接", padding=10)
        conn_frame.pack(fill=tk.X, pady=(0, 10))

        config_row = ttk.Frame(conn_frame)
        config_row.grid(row=0, column=0, columnspan=8, sticky=tk.W + tk.E, pady=(0, 5))

        ttk.Label(config_row, text="快速连接:").pack(side=tk.LEFT, padx=(0, 5))

        self.config_var = tk.StringVar()
        self.config_combo = ttk.Combobox(config_row, textvariable=self.config_var,
                                         state="readonly", width=20)
        self.config_combo.pack(side=tk.LEFT, padx=(0, 5))
        self.config_combo.bind("<<ComboboxSelected>>", self.on_config_selected)

        ttk.Button(config_row, text="加载", command=self.load_selected_config).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(config_row, text="保存", command=self.save_current_config).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(config_row, text="删除", command=self.delete_selected_config).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(config_row, text="刷新", command=self.refresh_config_list).pack(side=tk.LEFT)

        # 数据库类型选择
        ttk.Label(conn_frame, text="数据库类型:").grid(row=1, column=0, sticky=tk.W, padx=(0, 5))
        self.db_type_var = tk.StringVar(value="sqlite")
        db_type_combo = ttk.Combobox(conn_frame, textvariable=self.db_type_var,
                                     values=["sqlite", "mysql", "postgresql"], state="readonly", width=15)
        db_type_combo.grid(row=1, column=1, padx=(0, 10))
        db_type_combo.bind("<<ComboboxSelected>>", self.on_db_type_change)

        # 数据库文件/主机
        ttk.Label(conn_frame, text="主机/文件:").grid(row=1, column=2, sticky=tk.W, padx=(0, 5))
        self.host_var = tk.StringVar(value="test_database.db")
        self.host_entry = ttk.Entry(conn_frame, textvariable=self.host_var, width=20)
        self.host_entry.grid(row=1, column=3, padx=(0, 10))

        # 浏览文件按钮（SQLite）
        self.browse_btn = ttk.Button(conn_frame, text="浏览", command=self.browse_database_file)
        self.browse_btn.grid(row=1, column=4, padx=(0, 10))

        # 端口
        ttk.Label(conn_frame, text="端口:").grid(row=1, column=5, sticky=tk.W, padx=(0, 5))
        self.port_var = tk.StringVar()
        self.port_entry = ttk.Entry(conn_frame, textvariable=self.port_var, width=8)
        self.port_entry.grid(row=1, column=6, padx=(0, 10))

        # 第二行：数据库名、用户名、密码
        ttk.Label(conn_frame, text="数据库:").grid(row=2, column=0, sticky=tk.W, padx=(0, 5), pady=(5, 0))
        self.database_var = tk.StringVar()
        self.database_entry = ttk.Entry(conn_frame, textvariable=self.database_var, width=15)
        self.database_entry.grid(row=2, column=1, padx=(0, 10), pady=(5, 0))

        ttk.Label(conn_frame, text="用户名:").grid(row=2, column=2, sticky=tk.W, padx=(0, 5), pady=(5, 0))
        self.username_var = tk.StringVar()
        self.username_entry = ttk.Entry(conn_frame, textvariable=self.username_var, width=15)
        self.username_entry.grid(row=2, column=3, padx=(0, 10), pady=(5, 0))

        ttk.Label(conn_frame, text="密码:").grid(row=2, column=4, sticky=tk.W, padx=(0, 5), pady=(5, 0))
        self.password_var = tk.StringVar()
        self.password_entry = ttk.Entry(conn_frame, textvariable=self.password_var, show="*", width=15)
        self.password_entry.grid(row=2, column=5, padx=(0, 10), pady=(5, 0))

        # 连接按钮
        self.connect_btn = ttk.Button(conn_frame, text="连接", command=self.connect_database)
        self.connect_btn.grid(row=2, column=6, pady=(5, 0))

        # 连接状态
        # self.status_var = tk.StringVar(value="未连接")
        # self.status_label = ttk.Label(conn_frame, textvariable=self.status_var, foreground="red")
        # self.status_label.grid(row=1, column=7, rowspan=2, padx=(10, 0))

        # 初始化界面状态
        self.on_db_type_change()

    def create_main_content(self, parent):
        """创建主要内容区域"""
        # 创建笔记本控件（标签页）
        self.notebook = ttk.Notebook(parent)
        self.notebook.pack(fill=tk.BOTH, expand=True)

        # 表管理标签页
        self.create_table_management_tab()

        # 数据操作标签页
        self.create_data_operation_tab()

        # SQL查询标签页
        self.create_sql_query_tab()

        self.create_import_export_tab()

    def create_table_management_tab(self):
        """创建表管理标签页"""
        table_frame = ttk.Frame(self.notebook)
        self.notebook.add(table_frame, text="表管理")

        # 左侧：表列表
        left_frame = ttk.Frame(table_frame)
        left_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))

        ttk.Label(left_frame, text="数据库表:").pack(anchor=tk.W)

        # 表检索输入框
        search_frame = ttk.Frame(left_frame)
        search_frame.pack(fill=tk.X, pady=(5, 5))

        self.table_search_var = tk.StringVar()
        search_entry = ttk.Entry(search_frame, textvariable=self.table_search_var, width=22)
        search_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
        search_entry.bind('<KeyRelease>', self.apply_table_filter)

        clear_btn = ttk.Button(search_frame, text="清空", width=6, command=self.clear_table_search)
        clear_btn.pack(side=tk.RIGHT, padx=(5, 0))

        # 表列表
        self.table_listbox = tk.Listbox(left_frame, width=25, height=15)
        self.table_listbox.pack(fill=tk.Y, expand=True)
        self.table_listbox.bind("<<ListboxSelect>>", self.on_table_select)
        self.table_listbox.bind('<Double-Button-1>', self.on_table_double_click)

        # 表操作按钮
        table_btn_frame = ttk.Frame(left_frame)
        table_btn_frame.pack(fill=tk.X, pady=(5, 0))

        ttk.Button(table_btn_frame, text="刷新", command=self.refresh_tables).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(table_btn_frame, text="创建表", command=self.create_table_dialog).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(table_btn_frame, text="删除表", command=self.delete_table).pack(side=tk.LEFT)

        # 右侧：表结构信息
        right_frame = ttk.Frame(table_frame)
        right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)

        ttk.Label(right_frame, text="表结构:").pack(anchor=tk.W)

        # 表结构树形视图
        columns = ("列名", "类型", "可空", "主键", "唯一", "默认值")
        self.table_tree = ttk.Treeview(right_frame, columns=columns, show="headings", height=15)

        for col in columns:
            self.table_tree.heading(col, text=col)
            self.table_tree.column(col, width=100)

        # 添加滚动条
        table_scrollbar = ttk.Scrollbar(right_frame, orient=tk.VERTICAL, command=self.table_tree.yview)
        self.table_tree.configure(yscrollcommand=table_scrollbar.set)

        self.table_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        table_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

    def create_data_operation_tab(self):
        """创建数据操作标签页"""
        data_frame = ttk.Frame(self.notebook)
        self.notebook.add(data_frame, text="数据操作")

        # 顶部：表选择和操作按钮
        top_frame = ttk.Frame(data_frame)
        top_frame.pack(fill=tk.X, padx=10, pady=5)

        ttk.Label(top_frame, text="选择表:").pack(side=tk.LEFT, padx=(0, 5))
        self.data_table_var = tk.StringVar()
        self.data_table_combo = ttk.Combobox(top_frame, textvariable=self.data_table_var, width=20)
        self.data_table_combo.pack(side=tk.LEFT, padx=(0, 10))
        self.data_table_combo.bind('<<ComboboxSelected>>', self.on_data_table_select)

        ttk.Button(top_frame, text="查询", command=self.query_data_async).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(top_frame, text="插入", command=self.insert_data_dialog).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(top_frame, text="更新", command=self.update_data_dialog).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(top_frame, text="删除", command=self.delete_data_dialog).pack(side=tk.LEFT)

        # 中部：查询条件
        condition_frame = ttk.Frame(data_frame)
        condition_frame.pack(fill=tk.X, padx=10, pady=5)

        ttk.Label(condition_frame, text="WHERE:").pack(side=tk.LEFT, padx=(0, 5))
        self.where_var = tk.StringVar()
        self.where_entry = ttk.Entry(condition_frame, textvariable=self.where_var, width=50)
        self.where_entry.pack(side=tk.LEFT, padx=(0, 10))

        ttk.Label(condition_frame, text="ORDER BY:").pack(side=tk.LEFT, padx=(0, 5))
        self.order_var = tk.StringVar()
        self.order_entry = ttk.Entry(condition_frame, textvariable=self.order_var, width=20)
        self.order_entry.pack(side=tk.LEFT, padx=(0, 10))


        page_frame = ttk.Frame(data_frame)
        page_frame.pack(fill=tk.X, padx=10, pady=5)


        ttk.Label(page_frame, text="每页显示:").pack(side=tk.LEFT, padx=(0, 5))
        self.page_size_var = tk.StringVar(value="50")
        page_size_combo = ttk.Combobox(page_frame, textvariable=self.page_size_var,
                                      values=["20","50", "100", "200", "500", "1000"], width=8)
        page_size_combo.pack(side=tk.LEFT, padx=(0, 10))
        page_size_combo.bind('<<ComboboxSelected>>', self.on_page_size_change)

        ttk.Button(page_frame, text="首页", command=self.first_page).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(page_frame, text="上一页", command=self.prev_page).pack(side=tk.LEFT, padx=(0, 5))

        self.page_info_var = tk.StringVar(value="第 1 页，共 0 条记录")
        ttk.Label(page_frame, textvariable=self.page_info_var).pack(side=tk.LEFT, padx=(0, 10))

        ttk.Button(page_frame, text="下一页", command=self.next_page).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(page_frame, text="末页", command=self.last_page).pack(side=tk.LEFT, padx=(0, 5))

        self.loading_var = tk.StringVar()
        self.loading_label = ttk.Label(page_frame, textvariable=self.loading_var, foreground="blue")
        self.loading_label.pack(side=tk.RIGHT, padx=(10, 0))

        # 底部：数据显示
        self.create_data_display(data_frame)

    def query_data_async(self):
        """异步查询数据"""
        if self.loading:
            return

        table_name = self.data_table_var.get()
        if not table_name or not self.data_manager:
            return

        self.loading = True
        self.loading_var.set("正在加载...")

        # 在后台线程中执行查询
        future = self.executor.submit(self._query_data_background, table_name)

        # 定期检查查询结果
        self.root.after(100, lambda: self._check_query_result(future))

    def _query_data_background(self, table_name):
        """后台查询数据"""
        try:
            # 构建查询选项
            options = QueryOptions()

            self.page_size = int(self.page_size_var.get())
            offset = (self.current_page - 1) * self.page_size
            options.limit = self.page_size
            options.offset = offset

            # 处理WHERE条件
            where_text = self.where_var.get().strip()
            if where_text:
                options.conditions = self._parse_where_conditions(where_text)

            # 处理ORDER BY
            order_text = self.order_var.get().strip()
            if order_text:
                options.order_by = order_text

            # 获取总记录数
            count_options = QueryOptions()
            if where_text:
                count_options.conditions = options.conditions

            # 先获取总数
            total_query = f"SELECT COUNT(*) as total FROM {table_name}"
            if options.conditions:
                where_clause = " AND ".join([f"{c.column} {c.operator} ?" for c in options.conditions])
                total_query += f" WHERE {where_clause}"

            total_result = self.db_manager.execute_query(total_query,
                [c.value for c in options.conditions] if options.conditions else [])
            self.total_records = total_result[0]['total'] if total_result else 0

            # 执行分页查询
            results = self.data_manager.select(table_name, options=options)

            return {
                'success': True,
                'results': results,
                'total': self.total_records,
                'page': self.current_page,
                'page_size': self.page_size
            }

        except Exception as e:
            return {
                'success': False,
                'error': str(e)
            }

    def _check_query_result(self, future):
        """检查查询结果"""
        if future.done():
            self.loading = False
            self.loading_var.set("")

            try:
                result = future.result()
                if result['success']:
                    self.display_data_results_optimized(result['results'])
                    self.update_page_info()
                else:
                    messagebox.showerror("错误", f"查询数据失败: {result['error']}")
            except Exception as e:
                messagebox.showerror("错误", f"查询数据失败: {str(e)}")
        else:
            # 继续检查
            self.root.after(100, lambda: self._check_query_result(future))

    def _parse_where_conditions(self, where_text):
        """解析WHERE条件"""
        conditions = []
        operators = ['>=', '<=', '!=', '<>', '=', '>', '<', 'LIKE', 'IN']

        for op in operators:
            if op in where_text:
                parts = where_text.split(op, 1)
                if len(parts) == 2:
                    column = parts[0].strip()
                    value = parts[1].strip().strip("'\"")
                    if op == 'LIKE' and not value.startswith('%'):
                        value = f"%{value}%"
                    conditions.append(QueryCondition(column=column, operator=op, value=value))
                break

        return conditions

    def display_data_results_optimized(self, results):
        """优化的数据显示方法"""
        # 清空现有数据
        for item in self.data_tree.get_children():
            self.data_tree.delete(item)

        if not results:
            self.status_bar.config(text="查询结果为空")
            return

        # 设置列
        columns = list(results[0].keys())
        self.data_tree['columns'] = columns
        self.data_tree['show'] = 'headings'

        for col in columns:
            self.data_tree.heading(col, text=col)
            # 根据列名长度和数据类型设置合适的列宽
            col_width = max(120, len(col) * 10)
            self.data_tree.column(col, width=col_width, minwidth=80)

        items_to_insert = []
        for row in results:
            values = []
            for col in columns:
                value = row.get(col, "")
                # 限制显示长度避免界面卡顿
                if isinstance(value, str) and len(value) > 100:
                    value = value[:97] + "..."
                values.append(str(value))
            items_to_insert.append(values)

        # 分批插入数据
        batch_size = 50
        for i in range(0, len(items_to_insert), batch_size):
            batch = items_to_insert[i:i + batch_size]
            for values in batch:
                self.data_tree.insert("", tk.END, values=values)
            # 让界面有机会更新
            self.root.update_idletasks()

    def on_page_size_change(self, event=None):
        """页面大小改变事件"""
        self.current_page = 1
        self.query_data_async()

    def first_page(self):
        """首页"""
        if self.current_page != 1:
            self.current_page = 1
            self.query_data_async()

    def prev_page(self):
        """上一页"""
        if self.current_page > 1:
            self.current_page -= 1
            self.query_data_async()

    def next_page(self):
        """下一页"""
        max_page = (self.total_records + self.page_size - 1) // self.page_size
        if self.current_page < max_page:
            self.current_page += 1
            self.query_data_async()

    def last_page(self):
        """末页"""
        max_page = (self.total_records + self.page_size - 1) // self.page_size
        if self.current_page != max_page and max_page > 0:
            self.current_page = max_page
            self.query_data_async()

    def update_page_info(self):
        """更新分页信息"""
        max_page = (self.total_records + self.page_size - 1) // self.page_size if self.total_records > 0 else 1
        start_record = (self.current_page - 1) * self.page_size + 1
        end_record = min(self.current_page * self.page_size, self.total_records)

        info_text = f"第 {self.current_page}/{max_page} 页，显示 {start_record}-{end_record}，共 {self.total_records} 条记录"
        self.page_info_var.set(info_text)

    def on_data_table_select(self, event=None):
        """数据表选择事件"""
        self.current_page = 1
        self.total_records = 0
        self.data_cache.clear()
        # 自动查询第一页数据
        self.query_data_async()

    def query_data(self):
        """查询数据"""
        self.query_data_async()

    def create_data_display(self, parent):
        """创建数据显示区域"""
        display_frame = ttk.Frame(parent)
        display_frame.pack(fill=tk.BOTH, expand=True)

        display_frame.grid_rowconfigure(0, weight=1)
        display_frame.grid_columnconfigure(0, weight=1)

        # 数据树形视图
        self.data_tree = ttk.Treeview(display_frame, show="headings", selectmode="extended")

        # 添加滚动条
        data_v_scrollbar = ttk.Scrollbar(display_frame, orient=tk.VERTICAL, command=self.data_tree.yview)
        data_h_scrollbar = ttk.Scrollbar(display_frame, orient=tk.HORIZONTAL, command=self.data_tree.xview)

        self.data_tree.configure(yscrollcommand=data_v_scrollbar.set, xscrollcommand=data_h_scrollbar.set)

        # 布局
        self.data_tree.grid(row=0, column=0, sticky="nsew")
        data_v_scrollbar.grid(row=0, column=1, sticky="ns")
        data_h_scrollbar.grid(row=1, column=0, sticky="ew")

        # 右键菜单与复制快捷键
        self.data_tree_menu = tk.Menu(self.data_tree, tearoff=0)
        self.data_tree_menu.add_command(label="复制单元格", command=lambda: self.copy_cell())
        self.data_tree_menu.add_command(label="复制选中行", command=lambda: self.copy_selected_rows())
        self.data_tree_menu.add_command(label="复制选中行(含表头)", command=lambda: self.copy_selected_rows(include_headers=True))
        self.data_tree_menu.add_separator()
        self.data_tree_menu.add_command(label="复制列", command=lambda: self.copy_selected_column())
        self.data_tree_menu.add_command(label="复制全部", command=lambda: self.copy_all_rows())

        self.data_tree.bind('<Button-3>', self._on_data_tree_right_click)
        self.data_tree.bind('<Button-1>', self._on_data_tree_left_click)
        self.data_tree.bind('<Control-c>', lambda e: (self.copy_selected_rows()) or 'break')
        self.data_tree.bind('<Control-C>', lambda e: (self.copy_selected_rows()) or 'break')
        self.data_tree.bind('<Control-Shift-C>', lambda e: (self.copy_selected_rows(include_headers=True)) or 'break')

    def _on_data_tree_left_click(self, event):
        """记录最后点击的行和列，便于复制单元格/列"""
        if not hasattr(self, '_data_tree_last_row'):
            self._data_tree_last_row = None
        if not hasattr(self, '_data_tree_last_col'):
            self._data_tree_last_col = None
        self._data_tree_last_row = self.data_tree.identify_row(event.y)
        self._data_tree_last_col = self.data_tree.identify_column(event.x)

    def _on_data_tree_right_click(self, event):
        """右键弹出菜单，并将焦点移到指针所在的行"""
        row_id = self.data_tree.identify_row(event.y)
        col_id = self.data_tree.identify_column(event.x)
        if row_id:
            self.data_tree.selection_set(row_id)
            self.data_tree.focus(row_id)
        self._data_tree_last_row = row_id
        self._data_tree_last_col = col_id
        try:
            self.data_tree_menu.tk_popup(event.x_root, event.y_root)
        finally:
            self.data_tree_menu.grab_release()

    def copy_selected_rows(self, include_headers: bool = False):
        """复制选中行到剪贴板，支持是否包含表头"""
        columns = list(self.data_tree.cget('columns'))
        selected_items = self.data_tree.selection()
        if not selected_items:
            return
        lines = []
        if include_headers and columns:
            lines.append('\t'.join(str(col) for col in columns))
        for item_id in selected_items:
            values = self.data_tree.item(item_id, 'values')
            lines.append('\t'.join(str(v) for v in values))
        text = '\n'.join(lines)
        try:
            self.root.clipboard_clear()
            self.root.clipboard_append(text)
            self.status_bar.config(text=f"已复制 {len(selected_items)} 行到剪贴板")
        except Exception:
            pass

    def copy_cell(self):
        """复制当前单元格内容到剪贴板"""
        item_id = self.data_tree.focus() or getattr(self, '_data_tree_last_row', None)
        col_id = getattr(self, '_data_tree_last_col', None)
        if not item_id or not col_id or not col_id.startswith('#'):
            return
        try:
            index = int(col_id[1:]) - 1
        except ValueError:
            return
        values = self.data_tree.item(item_id, 'values')
        if index < 0 or index >= len(values):
            return
        value = values[index]
        try:
            self.root.clipboard_clear()
            self.root.clipboard_append(str(value))
            self.status_bar.config(text="已复制单元格到剪贴板")
        except Exception:
            pass

    def copy_selected_column(self):
        """复制当前列（选中行的该列；若无选中行则复制整列）"""
        col_id = getattr(self, '_data_tree_last_col', None)
        if not col_id or not col_id.startswith('#'):
            return
        try:
            index = int(col_id[1:]) - 1
        except ValueError:
            return
        selected_items = self.data_tree.selection()
        if not selected_items:
            selected_items = self.data_tree.get_children('')
        values_to_copy = []
        for item_id in selected_items:
            row_values = self.data_tree.item(item_id, 'values')
            if 0 <= index < len(row_values):
                values_to_copy.append(str(row_values[index]))
        if not values_to_copy:
            return
        try:
            self.root.clipboard_clear()
            self.root.clipboard_append('\n'.join(values_to_copy))
            self.status_bar.config(text=f"已复制 {len(values_to_copy)} 个值到剪贴板")
        except Exception:
            pass

    def copy_all_rows(self):
        """复制所有数据（含表头）"""
        columns = list(self.data_tree.cget('columns'))
        all_items = self.data_tree.get_children('')
        if not all_items:
            return
        lines = []
        if columns:
            lines.append('\t'.join(str(col) for col in columns))
        for item_id in all_items:
            values = self.data_tree.item(item_id, 'values')
            lines.append('\t'.join(str(v) for v in values))
        text = '\n'.join(lines)
        try:
            self.root.clipboard_clear()
            self.root.clipboard_append(text)
            self.status_bar.config(text=f"已复制全部，共 {len(all_items)} 行")
        except Exception:
            pass

    def create_sql_query_tab(self):
        """创建SQL查询标签页"""
        sql_frame = ttk.Frame(self.notebook)
        self.notebook.add(sql_frame, text="SQL查询")

        # 顶部：SQL输入区域
        top_frame = ttk.Frame(sql_frame)
        top_frame.pack(fill=tk.X, pady=(0, 10))

        ttk.Label(top_frame, text="SQL语句:").pack(anchor=tk.W)

        # SQL文本框
        sql_text_frame = ttk.Frame(top_frame)
        sql_text_frame.pack(fill=tk.X, pady=(5, 0))

        self.sql_text = tk.Text(sql_text_frame, height=8, wrap=tk.WORD)
        sql_text_scrollbar = ttk.Scrollbar(sql_text_frame, orient=tk.VERTICAL, command=self.sql_text.yview)
        self.sql_text.configure(yscrollcommand=sql_text_scrollbar.set)

        # SQL 关键字高亮样式
        try:
            self.sql_text.tag_configure('sql_keyword', foreground='#0B61A4')
        except Exception:
            pass

        self.sql_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        sql_text_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # SQL 提示：快捷键和输入联想
        self.sql_text.bind('<Control-space>', self.show_sql_suggestions)
        self.sql_text.bind('<KeyRelease>', self.on_sql_key_release)
        self.sql_text.bind('<Tab>', self.on_sql_complete_if_suggest_visible)
        self.sql_text.bind('<Escape>', lambda e: (self.hide_sql_suggestions(), 'break') if self.sql_suggest_win else None)
        self.sql_text.bind('<<Paste>>', lambda e: (self.root.after_idle(self.highlight_sql_keywords)))
        self.sql_text.bind('<FocusIn>', lambda e: self.root.after_idle(self.highlight_sql_keywords))

        # 执行按钮
        btn_frame = ttk.Frame(top_frame)
        btn_frame.pack(fill=tk.X, pady=(5, 0))

        ttk.Button(btn_frame, text="执行查询", command=self.execute_sql_query).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(btn_frame, text="执行语句", command=self.execute_sql_statement).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(btn_frame, text="清空", command=lambda: self.sql_text.delete(1.0, tk.END)).pack(side=tk.LEFT)

        # 底部：结果显示
        result_frame = ttk.LabelFrame(sql_frame, text="执行结果", padding=5)
        result_frame.pack(fill=tk.BOTH, expand=True)

        # 结果树形视图
        self.sql_result_tree = ttk.Treeview(result_frame, show="headings", selectmode="extended")

        sql_result_v_scrollbar = ttk.Scrollbar(result_frame, orient=tk.VERTICAL, command=self.sql_result_tree.yview)
        sql_result_h_scrollbar = ttk.Scrollbar(result_frame, orient=tk.HORIZONTAL, command=self.sql_result_tree.xview)

        self.sql_result_tree.configure(yscrollcommand=sql_result_v_scrollbar.set,
                                       xscrollcommand=sql_result_h_scrollbar.set)

        self.sql_result_tree.grid(row=0, column=0, sticky="nsew")
        sql_result_v_scrollbar.grid(row=0, column=1, sticky="ns")
        sql_result_h_scrollbar.grid(row=1, column=0, sticky="ew")

        result_frame.grid_rowconfigure(0, weight=1)
        result_frame.grid_columnconfigure(0, weight=1)

        # SQL 结果复制：右键菜单与快捷键
        self.sql_result_menu = tk.Menu(self.sql_result_tree, tearoff=0)
        self.sql_result_menu.add_command(label="复制单元格", command=lambda: self.sql_copy_cell())
        self.sql_result_menu.add_command(label="复制选中行", command=lambda: self.sql_copy_selected_rows())
        self.sql_result_menu.add_command(label="复制选中行(含表头)", command=lambda: self.sql_copy_selected_rows(include_headers=True))
        self.sql_result_menu.add_separator()
        self.sql_result_menu.add_command(label="复制列", command=lambda: self.sql_copy_selected_column())
        self.sql_result_menu.add_command(label="复制全部", command=lambda: self.sql_copy_all_rows())

        self.sql_result_tree.bind('<Button-3>', self._on_sql_result_right_click)
        self.sql_result_tree.bind('<Button-1>', self._on_sql_result_left_click)
        self.sql_result_tree.bind('<Control-c>', lambda e: (self.sql_copy_selected_rows()) or 'break')
        self.sql_result_tree.bind('<Control-C>', lambda e: (self.sql_copy_selected_rows()) or 'break')
        self.sql_result_tree.bind('<Control-Shift-C>', lambda e: (self.sql_copy_selected_rows(include_headers=True)) or 'break')

    def create_status_bar(self):
        """创建状态栏"""
        self.status_bar = ttk.Label(self.root, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)

    def on_db_type_change(self, event=None):
        """数据库类型改变事件"""
        db_type = self.db_type_var.get()

        if db_type == "sqlite":
            # SQLite模式
            self.browse_btn.config(state=tk.NORMAL)
            self.port_entry.config(state=tk.DISABLED)
            self.database_entry.config(state=tk.DISABLED)
            self.username_entry.config(state=tk.DISABLED)
            self.password_entry.config(state=tk.DISABLED)
            self.host_var.set("test_database.db")
        else:
            # MySQL/PostgreSQL模式
            self.browse_btn.config(state=tk.DISABLED)
            self.port_entry.config(state=tk.NORMAL)
            self.database_entry.config(state=tk.NORMAL)
            self.username_entry.config(state=tk.NORMAL)
            self.password_entry.config(state=tk.NORMAL)

            if db_type == "mysql":
                self.port_var.set("3306")
                self.host_var.set("localhost")
            elif db_type == "postgresql":
                self.port_var.set("5432")
                self.host_var.set("localhost")

    def browse_database_file(self):
        """浏览数据库文件"""
        filename = filedialog.askopenfilename(
            title="选择数据库文件",
            filetypes=[("SQLite数据库", "*.db"), ("所有文件", "*.*")]
        )
        if filename:
            self.host_var.set(filename)

    def connect_database(self):
        """连接或断开数据库"""
        if self.db_manager.connection:
            # 当前已连接，执行断开操作
            self.disconnect_database()
            return

        # 当前未连接，执行连接操作
        try:
            db_type_str = self.db_type_var.get()
            db_type = DatabaseType(db_type_str)

            if db_type == DatabaseType.SQLITE:
                config = DatabaseConfig(
                    db_type=db_type,
                    database=self.host_var.get()
                )
            else:
                port = int(self.port_var.get()) if self.port_var.get() else None
                config = DatabaseConfig(
                    db_type=db_type,
                    host=self.host_var.get(),
                    port=port,
                    database=self.database_var.get(),
                    username=self.username_var.get(),
                    password=self.password_var.get()
                )

            if self.db_manager.connect(config):
                self.table_manager = TableManager(self.db_manager)
                self.data_manager = DataManager(self.db_manager)
                self.import_export_manager = ImportExportManager(self.db_manager)
                self.update_connection_status(True)
                self.refresh_tables()
                self.status_bar.config(text="数据库连接成功")

                self.prompt_save_config(config)

                # 记录最后成功连接的配置名
                try:
                    self._save_last_config_marker()
                except Exception:
                    pass

                messagebox.showinfo("成功", "数据库连接成功！")
            else:
                self.update_connection_status(False)
                messagebox.showerror("错误", "数据库连接失败！")

        except Exception as e:
            self.update_connection_status(False)
            messagebox.showerror("错误", f"连接失败: {str(e)}")

    def disconnect_database(self):
        """断开数据库连接"""
        try:
            if self.db_manager.connection:
                self.db_manager.disconnect()

            # 重置相关对象
            self.table_manager = None
            self.data_manager = None
            self.import_export_manager = None
            self.current_table = None

            # 清空界面数据
            self.table_listbox.delete(0, tk.END)
            self.data_table_combo['values'] = []
            self.export_table_combo['values'] = []
            self.import_table_combo['values'] = []

            # 清空表结构显示
            for item in self.table_tree.get_children():
                self.table_tree.delete(item)

            # 清空数据显示
            for item in self.data_tree.get_children():
                self.data_tree.delete(item)

            # 清空SQL结果显示
            for item in self.sql_result_tree.get_children():
                self.sql_result_tree.delete(item)

            # 更新连接状态
            self.update_connection_status(False)
            self.status_bar.config(text="数据库已断开连接")
            messagebox.showinfo("成功", "数据库连接已断开！")

        except Exception as e:
            messagebox.showerror("错误", f"断开连接失败: {str(e)}")

    def refresh_tables(self):
        """刷新表列表"""
        if not self.db_manager.connection:
            return

        try:
            tables = self.db_manager.get_tables()
            # 保存完整表列表
            self.all_tables = tables

            # 根据检索内容更新表管理页面的列表
            self.apply_table_filter()

            # 更新数据操作页面的下拉框
            self.data_table_combo['values'] = tables

            self.export_table_combo['values'] = tables
            self.import_table_combo['values'] = tables

            self.status_bar.config(text=f"找到 {len(tables)} 个表")

        except Exception as e:
            messagebox.showerror("错误", f"刷新表列表失败: {str(e)}")

    def apply_table_filter(self, event=None):
        """根据检索关键字过滤左侧表列表"""
        keyword = self.table_search_var.get().strip().lower() if hasattr(self, 'table_search_var') else ''
        filtered = self.all_tables
        if keyword:
            filtered = [t for t in self.all_tables if keyword in str(t).lower()]

        # 记录当前选择以便尽量保持
        current_selection = None
        selection = self.table_listbox.curselection()
        if selection:
            current_selection = self.table_listbox.get(selection[0])

        self.table_listbox.delete(0, tk.END)
        for name in filtered:
            self.table_listbox.insert(tk.END, name)

        # 恢复已选表（若仍在过滤结果中）
        if current_selection and current_selection in filtered:
            index = filtered.index(current_selection)
            self.table_listbox.selection_set(index)
            self.table_listbox.activate(index)

    def clear_table_search(self):
        """清空表检索关键字并恢复列表"""
        if hasattr(self, 'table_search_var'):
            self.table_search_var.set('')
        self.apply_table_filter()

    def on_table_select(self, event):
        """表选择事件"""
        selection = self.table_listbox.curselection()
        if not selection:
            return

        table_name = self.table_listbox.get(selection[0])
        self.current_table = table_name
        self.show_table_structure(table_name)

    def on_table_double_click(self, event=None):
        """双击表名，跳转到数据操作并加载数据"""
        selection = self.table_listbox.curselection()
        if not selection:
            return
        table_name = self.table_listbox.get(selection[0])
        if not table_name:
            return
        # 切换到数据操作tab
        try:
            self.notebook.select(1)
        except Exception:
            pass
        # 设置目标表并触发查询
        self.data_table_var.set(table_name)
        self.on_data_table_select()

    def show_table_structure(self, table_name):
        """异步显示表结构，避免UI卡死"""
        if not self.table_manager:
            return

        # 在线程中执行耗时的表结构查询
        def worker():
            try:
                table_info = self.table_manager.get_table_info(table_name)
                if not table_info:
                    # 回到主线程清空UI
                    self.table_tree.after(0, self._update_table_tree, table_name, None, None)
                    return

                # 缓存列名用于 SQL 提示
                try:
                    self.table_columns_cache[table_name] = [col.name for col in table_info.columns]
                except Exception:
                    pass

                # 回到主线程更新UI
                self.table_tree.after(
                    0, self._update_table_tree, table_name, table_info, len(table_info.columns)
                )
            except Exception as e:
                # 回到主线程弹窗
                self.table_tree.after(
                    0, lambda: messagebox.showerror("错误", f"获取表结构失败: {str(e)}")
                )

        threading.Thread(target=worker, daemon=True).start()

    def _update_table_tree(self, table_name, table_info, column_count):
        # 清空现有数据
        for item in self.table_tree.get_children():
            self.table_tree.delete(item)

        if table_info is not None:
            # 添加列信息
            for col in table_info.columns:
                values = (
                    col.name,
                    col.data_type,
                    "是" if col.is_nullable else "否",
                    "是" if col.is_primary_key else "否",
                    "是" if col.is_unique else "否",
                    col.default_value or ""
                )
                self.table_tree.insert("", tk.END, values=values)
            self.status_bar.config(text="正在统计...")
            self.status_bar.config(
                text=f"表 '{table_name}' 有 {len(table_info.columns)} 列，{table_info.row_count} 行数据"
            )
        else:
            self.status_bar.config(text=f"未获取到表 '{table_name}' 的结构信息")
    # def show_table_structure(self, table_name):
    #     """显示表结构"""
    #     if not self.table_manager:
    #         return
    #
    #     try:
    #         table_info = self.table_manager.get_table_info(table_name)
    #         if not table_info:
    #             return
    #
    #         # 缓存列名用于 SQL 提示
    #         try:
    #             self.table_columns_cache[table_name] = [col.name for col in table_info.columns]
    #         except Exception:
    #             pass
    #
    #         # 清空现有数据
    #         for item in self.table_tree.get_children():
    #             self.table_tree.delete(item)
    #
    #         # 添加列信息
    #         for col in table_info.columns:
    #             values = (
    #                 col.name,
    #                 col.data_type,
    #                 "是" if col.is_nullable else "否",
    #                 "是" if col.is_primary_key else "否",
    #                 "是" if col.is_unique else "否",
    #                 col.default_value or ""
    #             )
    #             self.table_tree.insert("", tk.END, values=values)
    #
    #         self.status_bar.config(
    #             text=f"表 '{table_name}' 有 {len(table_info.columns)} 列，{table_info.row_count} 行数据")
    #
    #     except Exception as e:
    #         messagebox.showerror("错误", f"获取表结构失败: {str(e)}")

    # ============ SQL 提示相关方法 ============
    def on_sql_key_release(self, event=None):
        """键入时动态更新 SQL 提示"""
        if event is None:
            return
        # 输入字母/数字/下划线/点 或 退格 时尝试提示
        valid = False
        if event.keysym in ('BackSpace', 'underscore', 'period'):
            valid = True
        elif event.char and len(event.char) == 1 and (event.char.isalnum() or event.char == '_'):
            valid = True

        # 避免在组合键或控制键时弹出
        if event.keysym in ('Return', 'Escape'):
            if self.sql_suggest_win:
                self.hide_sql_suggestions()
            # 回车也刷新一次高亮
            self.root.after_idle(self.highlight_sql_keywords)
            return

        if valid:
            self.show_sql_suggestions()
        else:
            # 非输入类按键隐藏提示
            if event.keysym not in ('Up', 'Down', 'Left', 'Right', 'Control_L', 'Control_R', 'Shift_L', 'Shift_R'):
                if self.sql_suggest_win:
                    self.hide_sql_suggestions()

        # 异步刷新关键字高亮，避免卡顿
        self.root.after_idle(self.highlight_sql_keywords)

    def on_sql_complete_if_suggest_visible(self, event=None):
        """当提示可见时，按 Tab 进行补全"""
        if self.sql_suggest_win and self.sql_suggest_listbox:
            self.insert_selected_sql_suggestion()
            return 'break'
        return None

    def get_current_sql_prefix(self) -> str:
        """获取光标处正在输入的 token 前缀（字母/数字/下划线）"""
        try:
            line_start = self.sql_text.index('insert linestart')
            current = self.sql_text.get(line_start, 'insert')
            m = re.search(r'([A-Za-z0-9_]+)$', current)
            return m.group(1) if m else ''
        except Exception:
            return ''

    def gather_sql_suggestions(self, prefix: str) -> List[str]:
        """根据前缀收集关键字、表、列建议"""
        if not prefix:
            return []
        lower = prefix.lower()
        suggestions: List[str] = []

        # 关键字
        suggestions.extend([kw for kw in self.sql_keywords if kw.lower().startswith(lower)])

        # 表名
        suggestions.extend([t for t in self.all_tables if str(t).lower().startswith(lower)])

        # 当前表的列名
        current_table = self.data_table_var.get() or self.current_table
        if current_table:
            cols = self.table_columns_cache.get(current_table)
            if cols:
                suggestions.extend([c for c in cols if str(c).lower().startswith(lower)])

        # 去重，保持顺序
        seen = set()
        unique = []
        for s in suggestions:
            k = s.lower()
            if k not in seen:
                seen.add(k)
                unique.append(s)
        return unique[:50]

    def show_sql_suggestions(self, event=None):
        """在光标附近显示/更新 SQL 建议列表"""
        prefix = self.get_current_sql_prefix()
        items = self.gather_sql_suggestions(prefix)
        if not items:
            self.hide_sql_suggestions()
            return

        # 计算位置
        try:
            bbox = self.sql_text.bbox('insert')
        except Exception:
            bbox = None
        if not bbox:
            self.hide_sql_suggestions()
            return
        x, y, w, h = bbox
        absx = self.sql_text.winfo_rootx() + x
        absy = self.sql_text.winfo_rooty() + y + h

        # 创建或更新窗口
        if not self.sql_suggest_win:
            self.sql_suggest_win = tk.Toplevel(self.root)
            self.sql_suggest_win.overrideredirect(True)
            self.sql_suggest_win.attributes('-topmost', True)
            self.sql_suggest_listbox = tk.Listbox(self.sql_suggest_win, height=8)
            self.sql_suggest_listbox.pack(fill=tk.BOTH, expand=True)
            # 交互
            self.sql_suggest_listbox.bind('<Return>', lambda e: (self.insert_selected_sql_suggestion(), 'break'))
            self.sql_suggest_listbox.bind('<Tab>', lambda e: (self.insert_selected_sql_suggestion(), 'break'))
            self.sql_suggest_listbox.bind('<Escape>', lambda e: (self.hide_sql_suggestions(), 'break'))
            self.sql_suggest_listbox.bind('<Double-Button-1>', lambda e: self.insert_selected_sql_suggestion())
            self.sql_suggest_win.bind('<FocusOut>', lambda e: self.hide_sql_suggestions())

        self.sql_suggest_win.geometry(f"+{absx}+{absy}")

        # 填充数据
        self.sql_suggest_listbox.delete(0, tk.END)
        for it in items:
            self.sql_suggest_listbox.insert(tk.END, it)
        # 选中第一项
        if items:
            self.sql_suggest_listbox.selection_clear(0, tk.END)
            self.sql_suggest_listbox.selection_set(0)
            self.sql_suggest_listbox.activate(0)

        self.sql_suggest_win.deiconify()
        try:
            self.sql_suggest_win.lift()
        except Exception:
            pass

    def hide_sql_suggestions(self):
        """隐藏 SQL 建议窗口"""
        if self.sql_suggest_win:
            try:
                self.sql_suggest_win.destroy()
            except Exception:
                pass
            self.sql_suggest_win = None
            self.sql_suggest_listbox = None

    def insert_selected_sql_suggestion(self):
        """将当前选中的建议插入到光标位置，替换当前前缀"""
        if not (self.sql_suggest_win and self.sql_suggest_listbox):
            return
        sel = self.sql_suggest_listbox.curselection()
        if not sel:
            return
        value = self.sql_suggest_listbox.get(sel[0])
        # 删除前缀并插入
        prefix = self.get_current_sql_prefix()
        try:
            if prefix:
                self.sql_text.delete(f'insert - {len(prefix)} chars', 'insert')
            self.sql_text.insert('insert', value)
        except Exception:
            pass
        self.hide_sql_suggestions()
        # 插入后刷新高亮
        self.root.after_idle(self.highlight_sql_keywords)

    def highlight_sql_keywords(self):
        """为 SQL 文本中的关键字添加颜色高亮（大小写不敏感）"""
        try:
            text = self.sql_text.get('1.0', 'end-1c')
        except Exception:
            return

        # 清除旧标签
        try:
            self.sql_text.tag_remove('sql_keyword', '1.0', 'end')
        except Exception:
            pass

        if not text:
            return

        # 构建关键字正则（按词边界）
        try:
            # 使用已有关键字表
            kws = sorted(set(self.sql_keywords), key=lambda s: -len(s))
            pattern = r'\b(' + '|'.join(re.escape(k) for k in kws) + r')\b'
            for m in re.finditer(pattern, text, flags=re.IGNORECASE):
                start = f"1.0+{m.start()}c"
                end = f"1.0+{m.end()}c"
                self.sql_text.tag_add('sql_keyword', start, end)
        except Exception:
            pass

    def create_table_dialog(self):
        """创建表对话框"""
        if not self.table_manager:
            messagebox.showwarning("警告", "请先连接数据库")
            return

        # 这里简化实现，实际应该创建一个复杂的对话框
        table_name = simpledialog.askstring("创建表", "请输入表名:")
        if not table_name:
            return

        # 简单示例：创建一个基本的用户表
        columns = [
            ColumnInfo(name="id", data_type="INTEGER", is_primary_key=True, is_auto_increment=True, is_nullable=False),
            ColumnInfo(name="name", data_type="VARCHAR", max_length=100, is_nullable=False),
            ColumnInfo(name="email", data_type="VARCHAR", max_length=255, is_unique=True),
            ColumnInfo(name="created_at", data_type="DATETIME", default_value="CURRENT_TIMESTAMP")
        ]

        try:
            if self.table_manager.create_table(table_name, columns):
                messagebox.showinfo("成功", f"表 '{table_name}' 创建成功！")
                self.refresh_tables()
            else:
                messagebox.showerror("错误", "创建表失败")
        except Exception as e:
            messagebox.showerror("错误", f"创建表失败: {str(e)}")

    def delete_table(self):
        """删除表"""
        if not self.current_table:
            messagebox.showwarning("警告", "请先选择要删除的表")
            return

        if messagebox.askyesno("确认", f"确定要删除表 '{self.current_table}' 吗？\n此操作不可恢复！"):
            try:
                if self.table_manager.drop_table(self.current_table):
                    messagebox.showinfo("成功", f"表 '{self.current_table}' 删除成功！")
                    self.refresh_tables()
                    self.current_table = None
                    # 清空表结构显示
                    for item in self.table_tree.get_children():
                        self.table_tree.delete(item)
                else:
                    messagebox.showerror("错误", "删除表失败")
            except Exception as e:
                messagebox.showerror("错误", f"删除表失败: {str(e)}")


    def display_data_results(self, results):
        """显示数据查询结果"""
        # 清空现有数据
        for item in self.data_tree.get_children():
            self.data_tree.delete(item)

        if not results:
            self.status_bar.config(text="查询结果为空")
            return

        # 设置列
        columns = list(results[0].keys())
        self.data_tree['columns'] = columns
        self.data_tree['show'] = 'headings'

        for col in columns:
            self.data_tree.heading(col, text=col)
            self.data_tree.column(col, width=100)

        # 添加数据
        for row in results:
            values = [str(row.get(col, "")) for col in columns]
            self.data_tree.insert("", tk.END, values=values)

        self.status_bar.config(text=f"查询到 {len(results)} 条记录")

    def insert_data_dialog(self):
        """插入数据对话框"""
        table_name = self.data_table_var.get()
        if not table_name:
            messagebox.showwarning("警告", "请先选择表")
            return

        # 简化实现：弹出输入对话框
        messagebox.showinfo("提示", "插入数据功能需要更复杂的对话框实现")

    def update_data_dialog(self):
        """更新数据对话框"""
        messagebox.showinfo("提示", "更新数据功能需要更复杂的对话框实现")

    def delete_data_dialog(self):
        """删除数据对话框"""
        messagebox.showinfo("提示", "删除数据功能需要更复杂的对话框实现")

    def execute_sql_query(self):
        """执行SQL查询"""
        if not self.db_manager.connection:
            messagebox.showwarning("警告", "请先连接数据库")
            return

        sql = self.sql_text.get(1.0, tk.END).strip()
        if not sql:
            messagebox.showwarning("警告", "请输入SQL语句")
            return

        try:
            results = self.db_manager.execute_query(sql)
            self.display_sql_results(results)
            self.status_bar.config(text=f"查询执行成功，返回 {len(results)} 条记录")
        except Exception as e:
            messagebox.showerror("错误", f"SQL查询执行失败: {str(e)}")

    def execute_sql_statement(self):
        """执行SQL语句"""
        if not self.db_manager.connection:
            messagebox.showwarning("警告", "请先连接数据库")
            return

        sql = self.sql_text.get(1.0, tk.END).strip()
        if not sql:
            messagebox.showwarning("警告", "请输入SQL语句")
            return

        try:
            affected_rows = self.db_manager.execute_non_query(sql)
            messagebox.showinfo("成功", f"SQL语句执行成功，影响 {affected_rows} 行")
            self.status_bar.config(text=f"语句执行成功，影响 {affected_rows} 行")
            # 刷新表列表（可能有新表创建）
            self.refresh_tables()
        except Exception as e:
            messagebox.showerror("错误", f"SQL语句执行失败: {str(e)}")

    def display_sql_results(self, results):
        """显示SQL查询结果"""
        # 清空现有数据
        for item in self.sql_result_tree.get_children():
            self.sql_result_tree.delete(item)

        if not results:
            return

        # 设置列
        columns = list(results[0].keys())
        self.sql_result_tree['columns'] = columns
        self.sql_result_tree['show'] = 'headings'

        for col in columns:
            self.sql_result_tree.heading(col, text=col)
            self.sql_result_tree.column(col, width=100)

        # 添加数据
        for row in results:
            values = [str(row.get(col, "")) for col in columns]
            self.sql_result_tree.insert("", tk.END, values=values)

    # ------- SQL 结果复制辅助 -------
    def _on_sql_result_left_click(self, event):
        if not hasattr(self, '_sql_result_last_row'):
            self._sql_result_last_row = None
        if not hasattr(self, '_sql_result_last_col'):
            self._sql_result_last_col = None
        self._sql_result_last_row = self.sql_result_tree.identify_row(event.y)
        self._sql_result_last_col = self.sql_result_tree.identify_column(event.x)

    def _on_sql_result_right_click(self, event):
        row_id = self.sql_result_tree.identify_row(event.y)
        col_id = self.sql_result_tree.identify_column(event.x)
        if row_id:
            self.sql_result_tree.selection_set(row_id)
            self.sql_result_tree.focus(row_id)
        self._sql_result_last_row = row_id
        self._sql_result_last_col = col_id
        try:
            self.sql_result_menu.tk_popup(event.x_root, event.y_root)
        finally:
            self.sql_result_menu.grab_release()

    def sql_copy_selected_rows(self, include_headers: bool = False):
        columns = list(self.sql_result_tree.cget('columns'))
        selected_items = self.sql_result_tree.selection()
        if not selected_items:
            return
        lines = []
        if include_headers and columns:
            lines.append('\t'.join(str(col) for col in columns))
        for item_id in selected_items:
            values = self.sql_result_tree.item(item_id, 'values')
            lines.append('\t'.join(str(v) for v in values))
        text = '\n'.join(lines)
        try:
            self.root.clipboard_clear()
            self.root.clipboard_append(text)
            self.status_bar.config(text=f"已复制 {len(selected_items)} 行到剪贴板")
        except Exception:
            pass

    def sql_copy_cell(self):
        item_id = self.sql_result_tree.focus() or getattr(self, '_sql_result_last_row', None)
        col_id = getattr(self, '_sql_result_last_col', None)
        if not item_id or not col_id or not col_id.startswith('#'):
            return
        try:
            index = int(col_id[1:]) - 1
        except ValueError:
            return
        values = self.sql_result_tree.item(item_id, 'values')
        if index < 0 or index >= len(values):
            return
        value = values[index]
        try:
            self.root.clipboard_clear()
            self.root.clipboard_append(str(value))
            self.status_bar.config(text="已复制单元格到剪贴板")
        except Exception:
            pass

    def sql_copy_selected_column(self):
        col_id = getattr(self, '_sql_result_last_col', None)
        if not col_id or not col_id.startswith('#'):
            return
        try:
            index = int(col_id[1:]) - 1
        except ValueError:
            return
        selected_items = self.sql_result_tree.selection()
        if not selected_items:
            selected_items = self.sql_result_tree.get_children('')
        values_to_copy = []
        for item_id in selected_items:
            row_values = self.sql_result_tree.item(item_id, 'values')
            if 0 <= index < len(row_values):
                values_to_copy.append(str(row_values[index]))
        if not values_to_copy:
            return
        try:
            self.root.clipboard_clear()
            self.root.clipboard_append('\n'.join(values_to_copy))
            self.status_bar.config(text=f"已复制 {len(values_to_copy)} 个值到剪贴板")
        except Exception:
            pass

    def sql_copy_all_rows(self):
        columns = list(self.sql_result_tree.cget('columns'))
        all_items = self.sql_result_tree.get_children('')
        if not all_items:
            return
        lines = []
        if columns:
            lines.append('\t'.join(str(col) for col in columns))
        for item_id in all_items:
            values = self.sql_result_tree.item(item_id, 'values')
            lines.append('\t'.join(str(v) for v in values))
        text = '\n'.join(lines)
        try:
            self.root.clipboard_clear()
            self.root.clipboard_append(text)
            self.status_bar.config(text=f"已复制全部，共 {len(all_items)} 行")
        except Exception:
            pass

    def create_import_export_tab(self):
        """创建导入导出标签页"""
        ie_frame = ttk.Frame(self.notebook)
        self.notebook.add(ie_frame, text="导入导出")

        # 创建左右分栏
        left_frame = ttk.Frame(ie_frame)
        left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))

        right_frame = ttk.Frame(ie_frame)
        right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0))

        # 左侧：导出功能
        export_frame = ttk.LabelFrame(left_frame, text="数据导出", padding=10)
        export_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 5))

        # 导出表选择
        ttk.Label(export_frame, text="选择表:").pack(anchor=tk.W)
        self.export_table_var = tk.StringVar()
        self.export_table_combo = ttk.Combobox(export_frame, textvariable=self.export_table_var, state="readonly")
        self.export_table_combo.pack(fill=tk.X, pady=(5, 10))

        # 导出格式选择
        ttk.Label(export_frame, text="导出格式:").pack(anchor=tk.W)
        self.export_format_var = tk.StringVar(value="csv")
        format_frame = ttk.Frame(export_frame)
        format_frame.pack(fill=tk.X, pady=(5, 10))

        ttk.Radiobutton(format_frame, text="CSV", variable=self.export_format_var, value="csv").pack(side=tk.LEFT,
                                                                                                     padx=(0, 10))
        ttk.Radiobutton(format_frame, text="JSON", variable=self.export_format_var, value="json").pack(side=tk.LEFT,
                                                                                                       padx=(0, 10))
        ttk.Radiobutton(format_frame, text="SQL", variable=self.export_format_var, value="sql").pack(side=tk.LEFT)

        # 导出选项
        options_frame = ttk.Frame(export_frame)
        options_frame.pack(fill=tk.X, pady=(0, 10))

        self.include_structure_var = tk.BooleanVar(value=True)
        ttk.Checkbutton(options_frame, text="包含表结构", variable=self.include_structure_var).pack(anchor=tk.W)

        # 导出按钮
        export_btn_frame = ttk.Frame(export_frame)
        export_btn_frame.pack(fill=tk.X)

        ttk.Button(export_btn_frame, text="导出数据", command=self.export_data).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(export_btn_frame, text="备份数据库", command=self.backup_database).pack(side=tk.LEFT)

        # 右侧：导入功能
        import_frame = ttk.LabelFrame(right_frame, text="数据导入", padding=10)
        import_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 5))

        # 导入文件选择
        ttk.Label(import_frame, text="选择文件:").pack(anchor=tk.W)
        file_frame = ttk.Frame(import_frame)
        file_frame.pack(fill=tk.X, pady=(5, 10))

        self.import_file_var = tk.StringVar()
        self.import_file_entry = ttk.Entry(file_frame, textvariable=self.import_file_var)
        self.import_file_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))

        ttk.Button(file_frame, text="浏览", command=self.browse_import_file).pack(side=tk.RIGHT)

        # 目标表选择
        ttk.Label(import_frame, text="目标表:").pack(anchor=tk.W, pady=(10, 0))
        target_frame = ttk.Frame(import_frame)
        target_frame.pack(fill=tk.X, pady=(5, 10))

        self.import_table_var = tk.StringVar()
        self.import_table_combo = ttk.Combobox(target_frame, textvariable=self.import_table_var, state="readonly")
        self.import_table_combo.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))

        ttk.Button(target_frame, text="新建表", command=self.create_import_table).pack(side=tk.RIGHT)

        # 导入选项
        import_options_frame = ttk.Frame(import_frame)
        import_options_frame.pack(fill=tk.X, pady=(0, 10))

        self.create_table_var = tk.BooleanVar()
        self.clear_table_var = tk.BooleanVar()

        ttk.Checkbutton(import_options_frame, text="自动创建表", variable=self.create_table_var).pack(anchor=tk.W)
        ttk.Checkbutton(import_options_frame, text="清空现有数据", variable=self.clear_table_var).pack(anchor=tk.W)

        # 导入按钮
        import_btn_frame = ttk.Frame(import_frame)
        import_btn_frame.pack(fill=tk.X)

        ttk.Button(import_btn_frame, text="导入数据", command=self.import_data).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(import_btn_frame, text="恢复数据库", command=self.restore_database).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(import_btn_frame, text="执行SQL文件", command=self.execute_sql_file).pack(side=tk.LEFT)

        # 底部：操作日志
        log_frame = ttk.LabelFrame(ie_frame, text="操作日志", padding=5)
        log_frame.pack(fill=tk.X, pady=(5, 0))

        # 日志文本框
        log_text_frame = ttk.Frame(log_frame)
        log_text_frame.pack(fill=tk.X)

        self.log_text = tk.Text(log_text_frame, height=6, wrap=tk.WORD, state=tk.DISABLED)
        log_scrollbar = ttk.Scrollbar(log_text_frame, orient=tk.VERTICAL, command=self.log_text.yview)
        self.log_text.configure(yscrollcommand=log_scrollbar.set)

        self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        log_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

    def export_data(self):
        """导出数据"""
        if not self.import_export_manager:
            messagebox.showwarning("警告", "请先连接数据库")
            return

        table_name = self.export_table_var.get()
        if not table_name:
            messagebox.showwarning("警告", "请选择要导出的表")
            return

        format_type = self.export_format_var.get()

        # 选择保存文件
        if format_type == "csv":
            file_path = filedialog.asksaveasfilename(
                title="保存CSV文件",
                defaultextension=".csv",
                filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")]
            )
        elif format_type == "json":
            file_path = filedialog.asksaveasfilename(
                title="保存JSON文件",
                defaultextension=".json",
                filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
            )
        else:  # sql
            file_path = filedialog.asksaveasfilename(
                title="保存SQL文件",
                defaultextension=".sql",
                filetypes=[("SQL文件", "*.sql"), ("所有文件", "*.*")]
            )

        if not file_path:
            return

        try:
            self.log_message(f"开始导出表 '{table_name}' 到 {format_type.upper()} 格式...")

            if format_type == "csv":
                success = self.import_export_manager.export_to_csv(table_name, file_path)
            elif format_type == "json":
                success = self.import_export_manager.export_to_json(table_name, file_path)
            else:  # sql
                include_structure = self.include_structure_var.get()
                success = self.import_export_manager.export_to_sql(table_name, file_path, include_structure)

            if success:
                self.log_message(f"导出成功: {file_path}")
                messagebox.showinfo("成功", f"数据导出成功！\n文件: {file_path}")
            else:
                self.log_message("导出失败")
                messagebox.showerror("错误", "数据导出失败")

        except Exception as e:
            error_msg = f"导出失败: {str(e)}"
            self.log_message(error_msg)
            messagebox.showerror("错误", error_msg)

    def backup_database(self):
        """备份数据库"""
        if not self.import_export_manager:
            messagebox.showwarning("警告", "请先连接数据库")
            return

        file_path = filedialog.asksaveasfilename(
            title="保存备份文件",
            defaultextension=".json",
            filetypes=[("JSON备份文件", "*.json"), ("所有文件", "*.*")]
        )

        if not file_path:
            return

        try:
            self.log_message("开始备份数据库...")
            success = self.import_export_manager.backup_database(file_path)

            if success:
                self.log_message(f"数据库备份成功: {file_path}")
                messagebox.showinfo("成功", f"数据库备份成功！\n文件: {file_path}")
            else:
                self.log_message("数据库备份失败")
                messagebox.showerror("错误", "数据库备份失败")

        except Exception as e:
            error_msg = f"备份失败: {str(e)}"
            self.log_message(error_msg)
            messagebox.showerror("错误", error_msg)

    def browse_import_file(self):
        """浏览导入文件"""
        file_path = filedialog.askopenfilename(
            title="选择导入文件",
            filetypes=[
                ("所有支持格式", "*.csv;*.json;*.sql"),
                ("CSV文件", "*.csv"),
                ("JSON文件", "*.json"),
                ("SQL文件", "*.sql"),
                ("所有文件", "*.*")
            ]
        )

        if file_path:
            self.import_file_var.set(file_path)

    def create_import_table(self):
        """创建导入表"""
        table_name = simpledialog.askstring("创建表", "请输入新表名:")
        if table_name:
            self.import_table_var.set(table_name)

    def import_data(self):
        """导入数据"""
        if not self.import_export_manager:
            messagebox.showwarning("警告", "请先连接数据库")
            return

        file_path = self.import_file_var.get()
        if not file_path:
            messagebox.showwarning("警告", "请选择导入文件")
            return

        table_name = self.import_table_var.get()
        if not table_name:
            messagebox.showwarning("警告", "请选择目标表")
            return

        try:
            create_table = self.create_table_var.get()
            clear_table = self.clear_table_var.get()

            file_ext = os.path.splitext(file_path)[1].lower()

            self.log_message(f"开始导入文件 '{file_path}' 到表 '{table_name}'...")

            if file_ext == ".csv":
                count = self.import_export_manager.import_from_csv(
                    table_name, file_path, create_table, clear_table
                )
            elif file_ext == ".json":
                count = self.import_export_manager.import_from_json(
                    file_path, table_name, create_table, clear_table
                )
            else:
                messagebox.showerror("错误", "不支持的文件格式")
                return

            if count > 0:
                self.log_message(f"导入成功: {count} 条记录")
                messagebox.showinfo("成功", f"数据导入成功！\n导入了 {count} 条记录")
                self.refresh_tables()
            else:
                self.log_message("导入失败或无数据")
                messagebox.showwarning("警告", "导入失败或文件无数据")

        except Exception as e:
            error_msg = f"导入失败: {str(e)}"
            self.log_message(error_msg)
            messagebox.showerror("错误", error_msg)

    def restore_database(self):
        """恢复数据库"""
        if not self.import_export_manager:
            messagebox.showwarning("警告", "请先连接数据库")
            return

        file_path = filedialog.askopenfilename(
            title="选择备份文件",
            filetypes=[("JSON备份文件", "*.json"), ("所有文件", "*.*")]
        )

        if not file_path:
            return

        # 确认操作
        if not messagebox.askyesno("确认", "恢复数据库将覆盖现有数据，确定继续吗？"):
            return

        try:
            self.log_message(f"开始恢复数据库: {file_path}")
            success = self.import_export_manager.restore_database(file_path, drop_existing=True)

            if success:
                self.log_message("数据库恢复成功")
                messagebox.showinfo("成功", "数据库恢复成功！")
                self.refresh_tables()
            else:
                self.log_message("数据库恢复失败")
                messagebox.showerror("错误", "数据库恢复失败")

        except Exception as e:
            error_msg = f"恢复失败: {str(e)}"
            self.log_message(error_msg)
            messagebox.showerror("错误", error_msg)

    def execute_sql_file(self):
        """执行SQL文件"""
        if not self.import_export_manager:
            messagebox.showwarning("警告", "请先连接数据库")
            return

        file_path = filedialog.askopenfilename(
            title="选择SQL文件",
            filetypes=[("SQL文件", "*.sql"), ("所有文件", "*.*")]
        )

        if not file_path:
            return

        try:
            self.log_message(f"开始执行SQL文件: {file_path}")
            success = self.import_export_manager.execute_sql_file(file_path)

            if success:
                self.log_message("SQL文件执行成功")
                messagebox.showinfo("成功", "SQL文件执行成功！")
                self.refresh_tables()
            else:
                self.log_message("SQL文件执行失败")
                messagebox.showerror("错误", "SQL文件执行失败")

        except Exception as e:
            error_msg = f"执行失败: {str(e)}"
            self.log_message(error_msg)
            messagebox.showerror("错误", error_msg)

    def log_message(self, message: str):
        """添加日志消息"""
        if hasattr(self, 'log_text'):
            self.log_text.config(state=tk.NORMAL)
            timestamp = f"[{datetime.now().strftime('%H:%M:%S')}] "
            self.log_text.insert(tk.END, timestamp + message + "\n")
            self.log_text.see(tk.END)
            self.log_text.config(state=tk.DISABLED)
            self.root.update_idletasks()

    def update_connection_status(self, connected=None):
        """更新数据库连接状态显示"""
        if connected is None:
            connected = self.db_manager.connection is not None

        if connected:
            self.connect_btn.config(text="断开")
            # self.status_label.config(text="状态: 未连接")

        else:
            self.connect_btn.config(text="连接")
            # self.status_label.config(text="状态: 已连接")


    def refresh_config_list(self):
        """刷新配置列表"""
        config_names = self.config_manager.get_config_names()
        self.config_combo['values'] = config_names
        if config_names:
            self.config_combo.set('')

    def on_config_selected(self, event=None):
        """配置选择事件"""
        selected = self.config_var.get()
        if selected:
            info = self.config_manager.get_config_info(selected)
            if info:
                self.status_bar.config(text=f"已选择配置: {selected} ({info})")

    def load_selected_config(self):
        """加载选中的配置"""
        selected = self.config_var.get()
        if not selected:
            messagebox.showwarning("警告", "请先选择一个配置！")
            return

        config = self.config_manager.get_config(selected)
        if not config:
            messagebox.showerror("错误", "加载配置失败！")
            return

        # 填充界面
        self.db_type_var.set(config.db_type.value)
        self.on_db_type_change()  # 更新界面状态

        if config.db_type == DatabaseType.SQLITE:
            self.host_var.set(config.database or "")
        else:
            self.host_var.set(config.host or "")
            self.port_var.set(str(config.port) if config.port else "")
            self.database_var.set(config.database or "")
            self.username_var.set(config.username or "")
            self.password_var.set(config.password or "")

        self.status_bar.config(text=f"已加载配置: {selected}")
        messagebox.showinfo("成功", f"配置 '{selected}' 已加载！")

    def save_current_config(self):
        """保存当前配置"""
        name = simpledialog.askstring("保存配置", "请输入配置名称:")
        if not name:
            return

        if self.config_manager.config_exists(name):
            if not messagebox.askyesno("确认", f"配置 '{name}' 已存在，是否覆盖？"):
                return

        try:
            db_type_str = self.db_type_var.get()
            db_type = DatabaseType(db_type_str)

            if db_type == DatabaseType.SQLITE:
                config = DatabaseConfig(
                    db_type=db_type,
                    database=self.host_var.get()
                )
            else:
                port = int(self.port_var.get()) if self.port_var.get() else None
                config = DatabaseConfig(
                    db_type=db_type,
                    host=self.host_var.get(),
                    port=port,
                    database=self.database_var.get(),
                    username=self.username_var.get(),
                    password=self.password_var.get()
                )

            if self.config_manager.save_config(name, config, overwrite=True):
                self.refresh_config_list()
                self.config_var.set(name)
                # 保存最近一次配置名
                try:
                    self._save_last_config_marker(name)
                except Exception:
                    pass
                messagebox.showinfo("成功", f"配置 '{name}' 已保存！")
            else:
                messagebox.showerror("错误", "保存配置失败！")

        except Exception as e:
            messagebox.showerror("错误", f"保存配置失败: {str(e)}")

    def delete_selected_config(self):
        """删除选中的配置"""
        selected = self.config_var.get()
        if not selected:
            messagebox.showwarning("警告", "请先选择一个配置！")
            return

        if messagebox.askyesno("确认", f"确定要删除配置 '{selected}' 吗？"):
            if self.config_manager.delete_config(selected):
                self.refresh_config_list()
                self.config_var.set('')
                messagebox.showinfo("成功", f"配置 '{selected}' 已删除！")
            else:
                messagebox.showerror("错误", "删除配置失败！")

    def prompt_save_config(self, config):
        """提示保存配置"""
        if messagebox.askyesno("保存配置", "连接成功！是否保存此连接配置以便下次快速连接？"):
            name = simpledialog.askstring("保存配置", "请输入配置名称:")
            if name:
                if self.config_manager.save_config(name, config, overwrite=True):
                    self.refresh_config_list()
                    self.config_var.set(name)
                    # 保存最近一次配置名
                    try:
                        self._save_last_config_marker(name)
                    except Exception:
                        pass
                    messagebox.showinfo("成功", f"配置 '{name}' 已保存！")

    def run(self):
        """运行GUI应用"""
        self.root.mainloop()

    # ---------------- 自动连接最近配置 ----------------
    def _get_last_config_marker_path(self) -> str:
        base_dir = os.path.dirname(os.path.abspath(__file__))
        return os.path.join(base_dir, '.last_config.json')

    def _save_last_config_marker(self, name: Optional[str] = None):
        # 若未显式提供名称，则尝试使用当前选择
        if not name:
            name = self.config_var.get().strip()
        if not name:
            return
        data = {"last": name}
        try:
            with open(self._get_last_config_marker_path(), 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False)
        except Exception:
            pass

    def try_auto_connect_last_config(self):
        try:
            path = self._get_last_config_marker_path()
            if not os.path.exists(path):
                return
            with open(path, 'r', encoding='utf-8') as f:
                data = json.load(f)
            name = data.get('last')
            if not name:
                return
            config = self.config_manager.get_config(name)
            if not config:
                return
            # 填充并尝试连接（静默）
            self.db_type_var.set(config.db_type.value)
            self.on_db_type_change()
            if config.db_type == DatabaseType.SQLITE:
                self.host_var.set(config.database or "")
            else:
                self.host_var.set(config.host or "")
                self.port_var.set(str(config.port) if config.port else "")
                self.database_var.set(config.database or "")
                self.username_var.set(config.username or "")
                self.password_var.set(config.password or "")
            # 直接调用连接，不弹出保存对话
            if self.db_manager.connect(config):
                self.table_manager = TableManager(self.db_manager)
                self.data_manager = DataManager(self.db_manager)
                self.import_export_manager = ImportExportManager(self.db_manager)
                self.update_connection_status(True)
                self.refresh_tables()
                self.status_bar.config(text=f"已自动连接到配置: {name}")
        except Exception:
            # 静默失败，不影响启动
            pass
